package org.bangumibuddy.service.Impl;

import lombok.extern.slf4j.Slf4j;
import org.JQbitorrentClinet.ClientFactory;
import org.JQbitorrentClinet.interfaces.Client;
import org.JQbitorrentClinet.interfaces.TorrentManageProxy;
import org.JQbitorrentClinet.pojo.torrent.TorrentInfo;
import org.JQbitorrentClinet.request.torrentmanagement.AddNewTorrentParam;
import org.bangumibuddy.config.property.QbitorrentClientProperties;
import org.bangumibuddy.constant.DownloadStatus;
import org.bangumibuddy.entity.TorrentEntity;
import org.bangumibuddy.mapper.TorrentsMapper;
import org.bangumibuddy.pojo.DownloadInfo;
import org.bangumibuddy.pojo.TorrentFile;
import org.bangumibuddy.service.DownloadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Service
@Slf4j
public class QBitorrentDowanloadService implements DownloadService {

    org.JQbitorrentClinet.interfaces.Client client;

    ExecutorService executorService = Executors.newSingleThreadExecutor();

    @Autowired
    QbitorrentClientProperties properties;

    @Override
    public boolean addDownloadTasks(List<TorrentFile> torrentFiles) {
        log.trace("Add {} torrents", torrentFiles.size());
        Map<String, List<TorrentFile>> sortedTorrentFiles = getSortedTorrentFiles(torrentFiles);
        TorrentManageProxy torrentManageProxy = getClient().getTorrentManageProxy();
        for (Map.Entry<String, List<TorrentFile>> entry : sortedTorrentFiles.entrySet()) {
            List<String> urls = new ArrayList<>();
            for (TorrentFile torrentFile : entry.getValue()) {
                urls.add(torrentFile.getUrl());
            }
            //启动线程来添加种子
            executorService.submit(()->{
                AddNewTorrentParam addNewTorrentParam = AddNewTorrentParam.builder()
                        .urls(urls.toArray(new String[0]))
                        .savepath(entry.getKey())
                        .paused(true)
                        .build();

                torrentManageProxy.addNewTorrent(addNewTorrentParam);

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                //修改名字
                List<String> hashes = new ArrayList<>();
                for (TorrentFile torrentFile : entry.getValue()) {
                    TorrentInfo torrentInfo = torrentManageProxy.getTorrentInfo(torrentFile.getHash().toLowerCase());
                    if (torrentInfo == null) {
                        log.error("Can't find torrent info for hash: {}", torrentFile.getHash());
                        continue;
                    }
                    torrentManageProxy.renameFiles(torrentFile.getHash(), torrentInfo.getName(), torrentFile.getFileName());
                    hashes.add(torrentFile.getHash());
                }

                torrentManageProxy.resumeTorrent(hashes);
            });
        }

        return true;
    }

    @Override
    public List<DownloadInfo> getDownloadInfoByHashes(List<String> hashes) {
        List<TorrentInfo> torrentInfo = getClient().getTorrentManageProxy().getTorrentInfo(hashes);
        List<DownloadInfo> downloadInfos = new ArrayList<>();
        for (TorrentInfo info : torrentInfo) {
            DownloadInfo downloadInfo = new DownloadInfo();
            downloadInfo.setHash(info.getHash());
            downloadInfo.setTorrentName(info.getName());
            downloadInfo.setDownloadSpeed(info.getDlspeed());
            downloadInfo.setDownloadedSize(info.getDownloaded());
            downloadInfo.setEstimatedTime(info.getEta());
            downloadInfo.setTotalSize(info.getTotal_size());
            downloadInfo.setStatus(ConvertStatus(info.getState()));
            downloadInfos.add(downloadInfo);
        }

        return downloadInfos;
    }

    @Override
    public boolean pause(String hash) {
        return getClient().getTorrentManageProxy().pauseTorrent(Arrays.asList(hash));
    }

    @Override
    public boolean resume(String hash) {
        return getClient().getTorrentManageProxy().resumeTorrent(Arrays.asList(hash));
    }

    private DownloadStatus ConvertStatus(String status) {
        switch (status) {
            case "downloading":
                return DownloadStatus.DOWNLOADING;
            case "pausedDL":
                return DownloadStatus.PAUSED_DL;
            case "stalledDL":
                return DownloadStatus.STALLED_DL;
            case "checkingDL":
                return DownloadStatus.CHECKING_DL;
            case "queuedDL":
                return DownloadStatus.QUEUED_DL;
            case "forcedDL":
                return DownloadStatus.FORCED_DL;
            case "queuedUP":
            case "uploading":
            case "forcedUP":
            case "checkingUP":
            case "stalledUP":
            case "pausedUP":
                return DownloadStatus.FINISHED;
            case "allocating":
                return DownloadStatus.ALLOCATING;
            case "checkingResumeData":
                return DownloadStatus.CHECKING_RESUME_DATA;
            default:
                return DownloadStatus.UNKNOWN;
        }
    }

    Map<String,List<TorrentFile>> getSortedTorrentFiles(List<TorrentFile> torrentFiles) {
        Map<String, List<TorrentFile>> sortedTorrentFiles = new HashMap<>();

        for (TorrentFile torrentFile : torrentFiles) {
            String savePath = torrentFile.getSavePath();

            if (!sortedTorrentFiles.containsKey(savePath)) {
                sortedTorrentFiles.put(savePath, new ArrayList<>());
            }

            sortedTorrentFiles.get(savePath).add(torrentFile);
        }

        return sortedTorrentFiles;
    }

    private Client getClient()
    {
        if(client == null)
        {
            client = ClientFactory.createClient(properties.getHost(), properties.getPort(), properties.getUsername(), properties.getPassword());
        }

        return client;
    }
}
